package link.anycar.study.current.barrier;

import java.time.Instant;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * Check the FSC of
 *  public CyclicBarrier(int parties)
 *  public CyclicBarrier(int parties, Runnable barrierAction)
 * to see two different behavior of the barrier.
 *
 * When to use cyclic barrier?
 *  Like a running race, you all wait at the starting line until the gun fire.
 *  Like the start of a journey, the bus starts when all people on board.
 */
public class Barrier {

    static Random rnd = new Random(System.currentTimeMillis());

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(6, () -> System.out.println("Runners ready, fire gun!"));
        for (int i=0; i<5; i++) {
            int running = i + 1;
            new Thread(() -> {
                // the runner comes at different time
                sleep(rnd.nextInt(2) + 1);

                // waiting at the starting line until the gun fire
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println("Runner " + running + " starts running at " + Instant.now());
                sleep(rnd.nextInt(5) + 1);
                System.out.println("Runner " + running + " finishes running at " + Instant.now());
            }).start();
        }

        try {
            barrier.await(); // gun fires here
            sleep(7);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println();
        System.out.println("Start a new race ...");
        CyclicBarrier newBarrier = new CyclicBarrier(5);
        for (int i=5; i<10; i++) {
            int runner = i + 1;
            new Thread(() -> {
                // the runner comes at different time
                sleep(rnd.nextInt(2) + 1);

                // but starts and does not waiting at the start line for the gun fire
                System.out.println("Runner " + runner + " starts running at " + Instant.now());
                sleep(rnd.nextInt(5) + 1);

                // but instead waiting at the finishing line until everyone finish, strange right?
                try {
                    newBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println("Runner " + runner + " finishes running at " + Instant.now());
            }).start();
        }
    }

    static void sleep(int second) {
        try {
            Thread.sleep(second * 1000);
        } catch (Exception e){}
    }

}
